{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：什么是闭包？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "\n",
    "面试官想从这道题中考察面试人什么？\n",
    "前面我们提到，函数在 Python 中是一个一等对象，它们可以直接被引用。也就是说我们可以把函数对象赋值给某个变量，然后传给其它函数，可以访问函数属性，可以在一个函数中返回另一个函数，等等。而闭包就是其中之一。另外，我们在了解了闭包以后才能更好地去理解装饰器的用法。所以，你只有解锁了闭包这个技能，才能进一步解锁另外一个技能—装饰器。\n",
    "\n",
    "这个题中，面试官可能会针对闭包中的几个关键点去继续提问，比如：\n",
    "\n",
    "实现闭包的要素是哪些\n",
    "闭包中的自由变量的绑定\n",
    "nonlocal 的使用\n",
    "答案\n",
    "在一个内部函数中，对外部作用域的变量进行引用，(并且一般外部函数的返回值为内部函数)，那么内部函数就被认为是闭包。简而言之，闭包指延伸作用域的函数。\n",
    "\n",
    "拓展\n",
    "闭包有两个要素：\n",
    "\n",
    "嵌套在另一个函数中的函数\n",
    "自由变量的引用\n",
    "我们进一步了解以上两点。\n",
    "\n",
    "def plus_one():\n",
    "    v = 1\n",
    "    def add(new_value):\n",
    "        return v + new_value\n",
    "\n",
    "    return add # 这里返回一个函数对象\n",
    "\n",
    ">>> a = plus_one() # a是一个函数对象，对函数对象的调用，要使用括号\n",
    ">>> a(1)\n",
    ">>> a(3)\n",
    "由此我们可以发现，闭包的最大特点是可以将父函数的变量与内部函数绑定，并返回绑定变量后的函数（也即闭包），此时即便生成闭包的环境（父函数）已经释放，闭包仍然存在，绑定的变量不会因为父函数被释放了也随之释放，而是，它可以继续被内部函数继续引用。\n",
    "\n",
    "在闭包中，新手经常会犯一个错误，把自由变量隐式地变成了局部变量，即在内部函数给外部变量赋值，这就等同于告诉 Python，这个变量是一个局部变量，而非自由变量。那我如果实在想继续引用那个外部变量并且要给它赋值，这怎么解决呢？答案是nonlocal。\n",
    "\n",
    "def average():\n",
    "    total = 0\n",
    "    counter = 0\n",
    "    def calculate(new_value):\n",
    "        nonlocal total  # 告诉Python这个变量是自由变量\n",
    "        nonlocal counter\n",
    "        total += new_value\n",
    "        counter += 1\n",
    "        return total / counter\n",
    "    return calculate\n",
    "\n",
    ">>> test = average()\n",
    ">>> test(1)\n",
    "1.0\n",
    ">>> test(2)\n",
    "1.5\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}